﻿#include "cmd_projection.h"
#include "ais/ais_pointcloud.h"
#include "algo/algo_projection.h"
#include "topo/topo_all.h"

CAGD::CmdStatus CAGD::CmdPointCurve::Perform()
{
    if (m_objs.size() < 2)
        return CmdStatus::TERMINATE;

    auto v = m_objs[m_objs.size() - 2];
    auto c = m_objs[m_objs.size() - 1];

    if (v->Type() != TopoType::VERTEX || c->Type() != TopoType::EDGE)
        return CmdStatus::TERMINATE;

    auto vertex = std::static_pointer_cast<TopoVertex>(v);
    auto edge = std::static_pointer_cast<TopoEdge>(c);

    m_point = vertex->Geom();
    m_curve = edge->Geom();

    return CmdSelector::Perform();
}

CAGD::CmdStatus CAGD::CmdNearPointCurve::Perform()
{
    if (CmdPointCurve::Perform() == CmdStatus::PERFORM)
    {
        double t;
        BPnt Q;
        double d = Projection::Near(m_curve, m_point->Point(), t, Q, CAGD_ABS_ACC);

        Handle(GeomPoint) P(new GeomPoint(m_curve->Value(t)));
        Handle(AIS_Point) ap(new AIS_Point(P));
        m_context.CreateObject(ap);

        return CmdStatus::PERFORM;
    }
    return CmdStatus::TERMINATE;
}

CAGD::CmdStatus CAGD::CmdOrthoPointCurve::Perform()
{
    if (CmdPointCurve::Perform() == CmdStatus::PERFORM)
    {
        double t = (m_curve->FirstParameter() + m_curve->LastParameter()) / 2;
        BPnt Q;
        double d = Projection::Ortho2(m_curve, m_point->Point(), t, Q, CAGD_ABS_ACC);

        Handle(GeomPoint) P(new GeomPoint(m_curve->Value(t)));
        Handle(AIS_Point) ap(new AIS_Point(P));
        m_context.CreateObject(ap);

        return CmdStatus::PERFORM;
    }
    return CmdStatus::TERMINATE;
}

CAGD::CmdStatus CAGD::CmdPointSurface::Perform()
{
    if (m_objs.size() < 2)
        return CmdStatus::TERMINATE;

    auto v = m_objs[m_objs.size() - 2];
    auto c = m_objs[m_objs.size() - 1];

    if (v->Type() != TopoType::VERTEX || c->Type() != TopoType::FACE)
        return CmdStatus::TERMINATE;

    auto vertex = std::static_pointer_cast<TopoVertex>(v);
    auto face = std::static_pointer_cast<TopoFace>(c);

    m_point = vertex->Geom();
    m_surface = face->Geom();

    return CmdSelector::Perform();
}

CAGD::CmdStatus CAGD::CmdNearPointSurface::Perform()
{
    if (CmdPointSurface::Perform() == CmdStatus::PERFORM)
    {
        double u1, u2, v1, v2;
        m_surface->Bounds(u1, u2, v1, v2);
        double u = (u1 + u2) / 2;
        double v = (v1 + v2) / 2;

        BPnt Q;
        double d = Projection::Near(m_surface, m_point->Point(), u, v, Q, CAGD_ABS_ACC);

        Handle(GeomPoint) P(new GeomPoint(m_surface->Value(u, v)));
        Handle(AIS_Point) ap(new AIS_Point(P));
        m_context.CreateObject(ap);

        return CmdStatus::PERFORM;
    }
    return CmdStatus::TERMINATE;
}

CAGD::CmdStatus CAGD::CmdOrthoPointSurface::Perform()
{
    if (CmdPointSurface::Perform() == CmdStatus::PERFORM)
    {
        double u1, u2, v1, v2;
        m_surface->Bounds(u1, u2, v1, v2);
        double u = (u1 + u2) / 2;
        double v = (v1 + v2) / 2;

        BPnt Q;
        double d = Projection::Ortho2(m_surface, m_point->Point(), u, v, Q, CAGD_ABS_ACC);

        Handle(GeomPoint) P(new GeomPoint(m_surface->Value(u, v)));
        Handle(AIS_Point) ap(new AIS_Point(P));
        m_context.CreateObject(ap);

        return CmdStatus::PERFORM;
    }
    return CmdStatus::TERMINATE;
}

CAGD::CmdStatus CAGD::CmdTorusPointSurface::Perform()
{
    if (CmdPointSurface::Perform() == CmdStatus::PERFORM)
    {
        double u1, u2, v1, v2;
        m_surface->Bounds(u1, u2, v1, v2);
        double u = (u1 + u2) / 2;
        double v = (v1 + v2) / 2;

        BPnt Q;
        double d = Projection::Torus(m_surface, m_point->Point(), u, v, Q, CAGD_ABS_ACC);

        Handle(GeomPoint) P(new GeomPoint(m_surface->Value(u, v)));
        Handle(AIS_Point) ap(new AIS_Point(P));
        m_context.CreateObject(ap);

        return CmdStatus::PERFORM;
    }
    return CmdStatus::TERMINATE;
}

CAGD::CmdStatus CAGD::CmdCurveSurface::Perform()
{
    if (m_objs.size() < 2)
        return CmdStatus::TERMINATE;

    auto e = m_objs[m_objs.size() - 2];
    auto c = m_objs[m_objs.size() - 1];

    if (e->Type() != TopoType::EDGE || c->Type() != TopoType::FACE)
        return CmdStatus::TERMINATE;

    auto edge = std::static_pointer_cast<TopoEdge>(e);
    auto face = std::static_pointer_cast<TopoFace>(c);

    m_curve = edge->Geom();
    m_surface = face->Geom();

    return CmdSelector::Perform();
}

CAGD::CmdStatus CAGD::CmdMarchCurveSurface::Perform()
{
    if (CmdCurveSurface::Perform() == CmdStatus::PERFORM)
    {
        std::vector<BPnt2d> para;
        Projection::March(m_curve, m_surface, m_curve->FirstParameter(), m_curve->LastParameter(), 1000, CAGD_ABS_ACC,
                          para);

        std::vector<BPnt> projPoints;
        for (auto &p : para)
            projPoints.push_back(m_surface->Value(p));

        Handle(AIS_PointCloud) ap(new AIS_PointCloud(projPoints));
        m_context.CreateObject(ap);

        return CmdStatus::PERFORM;
    }
    return CmdStatus::TERMINATE;
}

CAGD::CmdStatus CAGD::CmdODECurveSurface::Perform()
{
    if (CmdCurveSurface::Perform() == CmdStatus::PERFORM)
    {
        std::vector<BPnt2d> para;
        Projection::ODE(m_curve, m_surface, m_curve->FirstParameter(), m_curve->LastParameter(), 1000, CAGD_ABS_ACC,
                        para);

        std::vector<BPnt> projPoints;
        for (auto &p : para)
            projPoints.push_back(m_surface->Value(p));

        Handle(AIS_PointCloud) ap(new AIS_PointCloud(projPoints));
        m_context.CreateObject(ap);

        return CmdStatus::PERFORM;
    }
    return CmdStatus::TERMINATE;
}
